package com.hardson.toolkit.util;

import com.hardson.toolkit.interfaces.consts.IEnumBase;
import com.sun.istack.internal.NotNull;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * 日期处理工具类
 *
 * @author hardson
 * @version 1.0
 */
public class DateUtil {
    public static String FMT_yyyy_MM_dd_T_HH_mm_ss = "yyyy-MM-dd'T'HH:mm:ss";
    public static String FMT_yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
    public static String FMT_yyyyMMdd_HH_mm_ss = "yyyyMMdd HH:mm:ss";
    public static String FMT_yyyyMMdd_HH_mm = "yyyyMMdd HH:mm";

    public static String FMT_yyyyMMddHHmmss = "yyyyMMddHHmmss";

    public static String FMT_yyyy_MM_dd_HH_mm = "yyyy-MM-dd HH:mm";

    public static String FMT_yyyy_MM_dd_HH = "yyyy-MM-dd HH";
    public static String FMT_yyyy = "yyyy";
    public static String FMT_yyyy_MM_dd = "yyyy-MM-dd";
    public static String FMT_yyyyMM = "yyyyMM";
    public static String FMT_yyyy_MM = "yyyy-MM";
    public static String FMT_yyyy_MM_dd_fs = "yyyy/MM/dd";
    // public static String FMT_yyyy_MM_dd_bs = "yyyy\\MM\\dd";
    public static String FMT_yyyyMMdd = "yyyyMMdd";

    public static String FMT_yyyy_MM_dd_HHmmss = "yyyy-MM-dd_HHmmss";

    public static String FMT_HH_mm_ss = "HH:mm:ss";
    public static String FMT_HH_mm_ss_SSS = "HH:mm:ss:SSS";

    public static String FMT_EE = "EE";

    public static String FMT_dd = "dd";

    public static String FMT_yyyy_MM_dd_hh24_mi_ss = "yyyy-MM-dd hh24:mi:ss";

    public static String FMT_cn_yyyy_MM_dd = "yyyy年MM月dd日";//经测试，兼容个位数的月份和日期
    public static String FMT_cn_yyyy_MM = "yyyy年MM月";
    public static String FMT_cn_yyyy = "yyyy年";

    public static int INITIALYEAR = 1700;

    public DateUtil() {
    }

    public static String timeToStr(long timeMillis, String format) {
        if (format == null || (format = format.trim()).isEmpty()) {
            format = FMT_HH_mm_ss_SSS;
        }
        long ms = timeMillis - TimeZone.getDefault().getRawOffset();
        SimpleDateFormat formatter = new SimpleDateFormat(format);
        return formatter.format(ms);
    }

    public static String dateToStr(final Date date) {
        return dateToStr(date, FMT_yyyy_MM_dd);
    }

    /**
     * 转换日期类型TO字符串类型
     *
     * @param date       Date 日期类型
     * @param dateFormat String 转换后的格式
     * @return String 处理后的日期字符串
     **/
    public static String dateToStr(final Date date, final String dateFormat) {
        if (date == null) {
            return "";
        }
        SimpleDateFormat df = new SimpleDateFormat(dateFormat);
        return df.format(date);
    }

    /**
     * java8本地日期时间类型转成字符串类型
     *
     * @param localDateTime 本地日期时间
     * @param dateFormat    日期时间格式
     * @return 日期时间字符串
     */
    public static String localDateTimeToStr(LocalDateTime localDateTime, String dateFormat) {
        if (localDateTime == null) {
            return "";
        }
        DateTimeFormatter df = DateTimeFormatter.ofPattern(dateFormat);
        return df.format(localDateTime);
    }

    /**
     * 转换日期类型TO字符串类型
     *
     * @param date       Date 日期类型
     * @param dateFormat String 转换后的格式
     * @return String 处理后的日期字符串
     **/
    public static String dateToStr(final long date, final String dateFormat) {
        return dateToStr(new Date(date), dateFormat);
    }

    public static Date strToDate(final String strDate) throws ParseException {
        return strToDate(strDate, FMT_yyyy_MM_dd);
    }

    /**
     * 转换字符串类型TO日期类型。转换将智能解析日期字符串获得日期格式，并支持中文日期字符串的解析。
     *
     * @param strDate 日期字符串
     * @return Date
     * @author hardson
     * @date 2024/9/4 下午2:27
     */
    public static Date strToAutoDate(final String strDate) throws ParseException {
        return strToDate(strDate, guessFormat(strDate));
    }

    /**
     * 转换字符串类型TO日期类型
     *
     * @param strDate    String 符串类型
     * @param dateFormat String 转换后的格式
     * @return String 处理后的日期字符串
     * @throws ParseException 转换异常
     **/
    public static Date strToDate(String strDate, final String dateFormat) throws ParseException {
        if (strDate == null || (strDate = strDate.trim()).isEmpty()) {
            return null;
        }
        SimpleDateFormat df = new SimpleDateFormat(dateFormat);
        return df.parse(strDate);
    }

    /**
     * java.util.Date --> java.time.LocalDateTime
     *
     * @param date 日期
     * @return 本地日期时间
     */
    public static LocalDateTime toLocalDateTime(final Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * java.util.Date --> java.time.LocalDate
     *
     * @param date 日期
     * @return 本地日期
     */
    public static LocalDate toLocalDate(final Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * java.util.Date --> java.time.LocalTime
     *
     * @param date 日期
     * @return 本地时间
     */
    public static LocalTime toLocalTime(final Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).toLocalTime();
    }

    /**
     * java.time.LocalDateTime --> java.util.Date
     *
     * @param localDateTime 本地日期时间
     * @return 日期
     */
    public static Date toDate(final LocalDateTime localDateTime) {
        Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
        return Date.from(instant);
    }

    /**
     * java.time.LocalDate --> java.util.Date
     *
     * @param localDate 本地日期
     * @return 日期
     */
    public static Date toDate(final LocalDate localDate) {
        Instant instant = localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
        return Date.from(instant);
    }

    /**
     * java.time.LocalTime --> java.util.Date
     *
     * @param localDate 本地日期
     * @param localTime 本地时间
     * @return 日期
     */
    public static Date toDate(final LocalDate localDate, final LocalTime localTime) {
        LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
        return toDate(localDateTime);
    }

    /**
     * 获取只有年份的Calendar（清空月、日、时、分、秒、毫秒)
     *
     * @return Calendar
     */
    public static Calendar getCalendarYearOnly() {
        Calendar cal = Calendar.getInstance(Locale.CHINA);
        cal.clear(Calendar.MILLISECOND);
        cal.clear(Calendar.SECOND);
        cal.clear(Calendar.MINUTE);
        cal.set(Calendar.HOUR_OF_DAY, 0);// 清除
        cal.clear(Calendar.DAY_OF_MONTH);
        cal.clear(Calendar.MONTH);
        return cal;
    }

    /**
     * 获取当前日期字符串
     *
     * @param dateFmt 日期格式
     * @return String 当前日期字符串
     */
    public static String getCurrentDateStr(final String dateFmt) {
        return dateToStr(System.currentTimeMillis(), dateFmt);
    }

    public static String nowString(final String dateFmt) {
        return getCurrentDateStr(dateFmt);
    }

    /**
     * 获取当前年度
     *
     * @return int 当前年度
     */
    public static int getCurrentYear() {
        return LocalDate.now().getYear();
    }

    /**
     * 取得日期的年份
     *
     * @param date 日期
     * @return yyyy 年份字符串
     */
    public static String getYear(@NotNull final Date date) {
        return dateToStr(date, DateUtil.FMT_yyyy);
    }

    /**
     * 取得日期的整型年份
     *
     * @param date 日期
     * @return yyyy 年份字符串
     */
    public static int getYearInt(@NotNull final Date date) {
        return Integer.parseInt(getYear(date));
    }

    /**
     * 通过传入的日期时间字符串，猜测日期时间的格式。
     * 不保证绝对准确，对于大多数常用、格式正确的日期时间字符串，能匹配出正确的日期格式。
     *
     * @param dateStr 日期部分以"-"相隔的日期时间字符串
     * @return String 日期时间格式字符串
     * @author hardson
     * @date 2023/8/8 下午3:38
     */
    public static String guessFormat(String dateStr) {
        if (dateStr == null || (dateStr = dateStr.trim()).isEmpty()) {
            return "";
        }
        if (dateStr.contains("T")) {
            return FMT_yyyy_MM_dd_T_HH_mm_ss;
        }
        if (dateStr.contains("年")) {
            return guessCnFormat(dateStr);
        }
        int len = dateStr.length();
        return (len > 19) ? FMT_yyyy_MM_dd_HH_mm_ss : DateFormat.getFormatByLen(len);
    }

    private static String guessCnFormat(String dateStr) {
        if (dateStr.contains("月")) {
            return dateStr.contains("日") ? FMT_cn_yyyy_MM_dd : FMT_cn_yyyy_MM;
        }
        return FMT_cn_yyyy;
    }
    @Getter
    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    private enum DateFormat implements IEnumBase<Integer> {
        LEN_4(4, FMT_yyyy),
        LEN_6(6, FMT_yyyyMM),
        LEN_7(7, FMT_yyyy_MM),
        LEN_8(8, FMT_yyyyMMdd),
        LEN_10(10, FMT_yyyy_MM_dd),
        LEN_13(13, FMT_yyyy_MM_dd_HH),
        LEN_14(14, FMT_yyyyMMdd_HH_mm),
        LEN_16(16, FMT_yyyy_MM_dd_HH_mm),
        LEN_17(17, FMT_yyyyMMdd_HH_mm_ss),
        LEN_19(19, FMT_yyyy_MM_dd_HH_mm_ss);
        private final Integer code;
        private final String format;

        @Override
        public String getName() {
            return this.format;
        }

        static String getFormatByLen(int len) {
            return IEnumBase.getNameByCode(values(), len);
        }
    }

    public static void main(final String[] args) throws ParseException {
        String dateStr = "2022年12月2日";
        String fmt = DateUtil.guessFormat(dateStr);
        Date date = DateUtil.strToDate(dateStr, fmt);
        System.out.println(DateUtil.dateToStr(date));
    }
}
